﻿using System;
using System.Linq;
using System.Transactions;
using WSPay.Common;
using WSPay.Common.Data;
using WSPay.Core.DTO;
using WSPay.Domain;

namespace WSPay.Application
{
    public class WSBService : BaseService, IWSBService
    {
        public WSBService(IUnitOfWork unitOfWork) : base(unitOfWork)
        {
        }

        /// <summary>
        /// 获得文史币账户、如果不存在、创建新的
        /// </summary>
        /// <param name="userID"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        private WSBAccount WSBAccountExitOrCreate(Guid userID, string userName)
        {
            WSBAccount account = _unitOfWork.D<WSBAccount>().Query().FirstOrDefault(x => x.UserID == userID);
            if (account == null)
            {
                account = new WSBAccount(userID, userName);
                _unitOfWork.D<WSBAccount>().Insert(account);
               // _unitOfWork.Commit();
            }
            return account;
        }

        public void CreateSystemDefaultWSBAccount()
        {
            var wstd = _unitOfWork.D<WSBAccount>().Query().FirstOrDefault(x => x.UserID == SystemWSBAccount.WSTD.UserID);
            if (wstd == null)
            {
                wstd = new WSBAccount(SystemWSBAccount.WSTD.UserID, SystemWSBAccount.WSTD.UserName);
                wstd.IsSystemAccount = true;
                _unitOfWork.D<WSBAccount>().Insert(wstd);
            }

            _unitOfWork.Commit();
        }

        /// <summary>
        /// 创建充值订单
        /// </summary>
        /// <param name="userID">用户ID</param>
        /// <param name="money">充值金额（单位：元）</param>
        /// <param name="source">充值来源</param>
        /// <param name="remark">备注</param>
        /// <returns></returns>
        public WSBRechargeOrder CreateWSBRechargeBill(ChargeDto dto)
        {
            //if (ValidateSignCharge(dto) != true)
            //{
            //    throw  new ApplicationException("验签失败！");
            //}
            //var payerAccount = WSBAccountExitOrCreate(dto.PayerID, dto.PayerName);
            var order = new WSBRechargeOrder();

            order.TotalPrice = dto.OrderMoney;
            order.PayAmount = dto.OrderMoney;
            order.RechargeState = RechargeState.Unpaid;
            order.RechargeSource = RechargeSource.AliPay;
            order.Remark = dto.Remark;
            order.UserID = dto.PayerID;
            order.Coin = dto.OrderMoney * WSPayConfig.Instance.WSBExchangeRate;
            order.PayerName = dto.PayerName;
            order.CreateTime = DateTime.Now;
            order.Plat = dto.Plat;
            order.UpdateTime = DateTime.Now;
            _unitOfWork.D<WSBRechargeOrder>().Insert(order);
            _unitOfWork.Commit();

            return order;
        }

        public WSBRechargeOrder CreateWSBRechargeBillForWeix(ChargeDto dto)
        {
            var order = new WSBRechargeOrder();

            order.TotalPrice = dto.OrderMoney;
            order.PayAmount = dto.OrderMoney;
            order.RechargeState = RechargeState.Unpaid;
            order.RechargeSource = RechargeSource.Weixin;
            order.Remark = dto.Remark;
            order.UserID = dto.PayerID;
            order.Coin = dto.OrderMoney * WSPayConfig.Instance.WSBExchangeRate;
            order.PayerName = dto.PayerName;
            order.CreateTime = DateTime.Now;
            order.Plat = dto.Plat;
            order.UpdateTime = DateTime.Now;
            _unitOfWork.D<WSBRechargeOrder>().Insert(order);
            _unitOfWork.Commit();

            return order;
        }

        public WSBRechargeOrder GetRechargeOrder(string orderId)
        {
            return _unitOfWork.D<WSBRechargeOrder>().ReadQuery().FirstOrDefault(x => x.OrderId == orderId);
        }

        public WSBRechargeOrder GetRechargeOrder(int id) {
            return _unitOfWork.D<WSBRechargeOrder>().ReadQuery().First(o => o.ID == id);
        }

        public void RechargeFailure(string orderId)
        {
            var data= _unitOfWork.D<WSBRechargeOrder>().Query().FirstOrDefault(x => x.OrderId == orderId);
            data.RechargeState=RechargeState.Failure;
            _unitOfWork.Commit();
        }

        /// <summary>
        /// 充值到文史币账户，汇率按照WSPayConfig.WSBExchangeRate配置
        /// </summary>
        /// <param name="rechargeOrder"></param>
        public void RechargeToWSBAccount(string rechargeOrderId,string alyOrderId)
        {
            var rechargeOrder = _unitOfWork.D<WSBRechargeOrder>().Query()
                .FirstOrDefault(x => x.OrderId == rechargeOrderId);
            if (rechargeOrder.RechargeState == RechargeState.Completed)
                throw new ArgumentException("无效的充值订单，充值订单状态：" + rechargeOrder.RechargeState.ToString());

            //人民币与文史币汇率
            //decimal wsbAmount = rechargeOrder.TotalPrice * WSPayConfig.Instance.WSBExchangeRate;
            decimal wsbAmount = rechargeOrder.Coin;
            Guid userID = rechargeOrder.UserID;

            var payerAccount = WSBAccountExitOrCreate(rechargeOrder.UserID, rechargeOrder.PayerName);
            WSBAccountSeq seq = new WSBAccountSeq(userID, payerAccount.ID);
            seq.PreAmount = payerAccount.Ammount;
            seq.FlowFlag = FlowFlag.Income;
            seq.ChangeType = ChangeType.Charge;

            payerAccount.Ammount += wsbAmount;
            payerAccount.CashAmmount += wsbAmount;
            payerAccount.LastUpdateTime = DateTime.Now;

            seq.CashAmount += wsbAmount;
            seq.Amount = payerAccount.Ammount;
           // seq.Note = "充值订单：" + rechargeOrder.ID;
            seq.PayInfo = "充值订单：" + rechargeOrder.ID;
            rechargeOrder.RechargeState = RechargeState.Completed;
            rechargeOrder.AliPayOrderId = alyOrderId;
            seq.Note = "文史币充值(单号"+rechargeOrder.ID+")";
            //_unitOfWork.D<WSBAccount>().Update(account);
            _unitOfWork.D<WSBAccountSeq>().Insert(seq);

            _unitOfWork.Commit();
        }

        public WSBPayMessageDTO WSBTransactions(WSBPayDTO payDto)
        {

            if (ValidateSign(payDto) != true)
                return WSBPayMessageDTO.Code1001;

            if (payDto.OrderMoney <= 0) return WSBPayMessageDTO.Code1021;


            var payerAccount = WSBAccountExitOrCreate(payDto.PayerID, payDto.PayerName);
            if (payerAccount == null) return WSBPayMessageDTO.Code1005;

            var payeeAccount = WSBAccountExitOrCreate(payDto.PayeeID, payDto.PayeeName);
            if (payeeAccount == null) return WSBPayMessageDTO.Code1006;
            if (!payeeAccount.IsSystemAccount && payDto.PayType == 1) return WSBPayMessageDTO.Code1007;

            using (TransactionScope scope = new TransactionScope())
            {

               

                var payBill = new WSBPayBill()
                {
                    ClientGuid = payDto.ClientID,
                    OrderNo = payDto.OrderNo,
                    OrderInfo = payDto.OrderInfo,
                    OrderMoney = payDto.OrderMoney,
                    TradeDate = payDto.TradeDate,
                    PayerID = payDto.PayerID,
                    PayerName = payDto.PayerName,
                    PayeeID = payDto.PayeeID,
                    PayeeName = payDto.PayeeName,
                    Remark = payDto.Remark,

                    #region  add by lyh
                    ChangeType = payDto.ChangeType,
                    ChannelName = payDto.ChannelName,

                    #endregion
                };
                //插入文史币支付交易单
                _unitOfWork.D<WSBPayBill>().Insert(payBill);

                //付款方余额不足
                
                if (payDto.ChangeType != ChangeType.Encourage && payerAccount.CashAmmount < payDto.OrderMoney) return WSBPayMessageDTO.Code1020;

                //付款方账户流水
                var payerAccountSeq = new WSBAccountSeq(payerAccount.UserID, payerAccount.ID)
                {

                    //ChangeType = ChangeType.Pay,
                    ChangeType =  payDto.ChangeType,
                    FlowFlag = FlowFlag.Output,
                    PayInfo = payDto.OrderNo + "+" + payDto.OrderInfo,
                    ChannelName = payDto.ChannelName,
                    Note = payDto.Remark
                };
                payerAccountSeq.PreAmount = payerAccount.Ammount;
                payerAccountSeq.CashAmount = payDto.OrderMoney;
                //收款方账户流水
                var payeeAccountSeq = new WSBAccountSeq(payeeAccount.UserID, payeeAccount.ID)
                {
                    //ChangeType = Common.ChangeType.Pay,
                    ChangeType = payDto.ChangeType,
                    FlowFlag = Common.FlowFlag.Income,
                    PayInfo = payDto.OrderNo + "+" + payDto.OrderInfo,
                    ChannelName = payDto.ChannelName,
                    Note = payDto.Remark
                    
                };
                payeeAccountSeq.PreAmount = payeeAccount.Ammount;
                payeeAccountSeq.CashAmount = payDto.OrderMoney;

                //付款方账户变动
                payerAccount.CashAmmount -= payDto.OrderMoney;
                payerAccount.Ammount -= payDto.OrderMoney;
                payerAccount.LastUpdateTime = DateTime.Now;
                //收款方账户变动
                payeeAccount.CashAmmount += payDto.OrderMoney;
                payeeAccount.Ammount += payDto.OrderMoney;
                payeeAccount.LastUpdateTime = DateTime.Now;

                payerAccountSeq.Amount = payerAccount.Ammount;
                payeeAccountSeq.Amount = payeeAccount.Ammount;

                //_unitOfWork.D<WSBAccount>().Update(payerAccount);
               //_unitOfWork.D<WSBAccount>().Update(payeeAccount);

                //插入账户流水
                _unitOfWork.D<WSBAccountSeq>().Insert(payerAccountSeq);
                _unitOfWork.D<WSBAccountSeq>().Insert(payeeAccountSeq);

                _unitOfWork.Commit();

                scope.Complete();
            }

            return WSBPayMessageDTO.Code0000;
        }

        private bool ValidateSign(WSBPayDTO paybilldto)
        {
            var clientInfo = _unitOfWork.Q<ClientInfo>().FirstOrDefault(x => x.Guid == paybilldto.ClientID);

            if (clientInfo == null) return false;

            var sp = "|";

            var signString = paybilldto.ClientID + sp +
                clientInfo.MD5Key + sp +
                paybilldto.OrderNo + sp +
                paybilldto.OrderInfo + sp +
                paybilldto.OrderMoney + sp +
                paybilldto.PayType+sp+
                paybilldto.PayerID + sp +
                paybilldto.PayerName + sp +
                paybilldto.PayeeID + sp +
                paybilldto.PayeeName + sp +
                paybilldto.TradeDate.ToString("yyyy-MM-dd HH:mm:ss") + sp +
                paybilldto.ChangeType + sp+
                paybilldto.ChannelName+sp+
                paybilldto.Remark;

            var sign = signString.Hash();

            if (string.IsNullOrEmpty(paybilldto.Sign)||!paybilldto.Sign.ToUpper().Equals(sign))
            {
                return false;
            }

            return true;
        }
        private bool ValidateSignCharge(ChargeDto paybilldto)
        {
            var clientInfo = _unitOfWork.Q<ClientInfo>().FirstOrDefault(x => x.Guid == paybilldto.ClientID);

            if (clientInfo == null) return false;

            var sp = "|";

            var signString = paybilldto.ClientID + sp +
                clientInfo.MD5Key + sp +
                paybilldto.OrderInfo + sp +
                paybilldto.OrderMoney + sp +
                paybilldto.PayType + sp +
                paybilldto.PayerID + sp +
                paybilldto.PayerName + sp +
                paybilldto.TradeDate.ToString("yyyy-MM-dd HH:mm:ss") + sp +
                paybilldto.Remark;

            var sign = signString.Hash();

            if (string.IsNullOrEmpty(paybilldto.Sign) || !paybilldto.Sign.ToUpper().Equals(sign))
            {
                return false;
            }

            return true;
        }


        public WSBAccount QueryWsbAccount(Guid userId, string userName) {
            var account =WSBAccountExitOrCreate(userId, userName);
            return account;

        }
        public IQueryable<WSBAccount> QueryWSBAccount()
        {
            return _unitOfWork.D<WSBAccount>().ReadQuery();
        }

        public IQueryable<WSBAccountSeq> QueryWSBAccountSeq()
        {
            return _unitOfWork.D<WSBAccountSeq>().ReadQuery();
        }
    }
}
